Last active
December 23, 2015 13:19
-
-
Save corecode/6641720 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
class CDCDesc < FunctionDesc | |
TypeName = "struct cdc_function_desc" | |
child_block :cdc | |
def initialize | |
super() | |
@ctrl_iface = interface(:ctrl_iface) { | |
bInterfaceClass :USB_DEV_CLASS_CDC | |
bInterfaceSubClass :USB_DEV_SUBCLASS_CDC_ACM | |
bInterfaceProtocol 0 | |
ep(:ctrl_ep) { | |
direction :in | |
type :intr | |
wMaxPacketSize :CDC_NOTICE_SIZE | |
bInterval 0xff | |
} | |
} | |
@data_iface = interface(:data_iface) { | |
bInterfaceClass :USB_DEV_CLASS_CDC_DCD | |
bInterfaceSubClass 0 | |
bInterfaceProtocol 0 | |
ep(:tx_ep) { | |
direction :in | |
type :bulk | |
wMaxPacketSize :CDC_TX_SIZE | |
bInterval 0xff | |
} | |
ep(:rx_ep) { | |
direction :out | |
type :bulk | |
wMaxPacketSize :CDC_RX_SIZE | |
bInterval 0xff | |
} | |
} | |
end | |
def gen_desc_init | |
<<_end_ | |
.#@var_name = { | |
#{@interface.map{|i| i.gen_desc_init}.join} | |
.cdc_header = { | |
.bLength = sizeof(struct cdc_desc_function_header_t), | |
.bDescriptorType = { | |
.id = USB_DESC_IFACE, | |
.type_type = USB_DESC_TYPE_CLASS | |
}, | |
.bDescriptorSubtype = USB_CDC_SUBTYPE_HEADER, | |
.bcdCDC = { .maj = 1, .min = 1 } | |
}, | |
.cdc_union = { | |
.bLength = sizeof(struct cdc_desc_function_union_t), | |
.bDescriptorType = { | |
.id = USB_DESC_IFACE, | |
.type_type = USB_DESC_TYPE_CLASS | |
}, | |
.bDescriptorSubtype = USB_CDC_SUBTYPE_UNION, | |
.bControlInterface = #{@ctrl_iface.ifacenum} | |
.bSubordinateInterface0 = #{@data_iface.ifacenum} | |
}, | |
}, | |
_end_ | |
end | |
end |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
require 'dsl' | |
class EndpointDesc < DslItem | |
field :direction, :enum => {:in => 1, :out => 0} | |
field :type, :enum => {:intr => :USB_EP_INTR, :bulk => :USB_EP_BULK} | |
field :wMaxPacketSize | |
field :bInterval | |
def initialize(name) | |
super() | |
@name = name | |
end | |
def renumber!(counts) | |
cname = "ep_#@direction".to_sym | |
@epnum = counts[cname] | |
counts.merge cname => @epnum + 1 | |
end | |
def gen_desc_init | |
<<_end_ | |
.#{@name.to_loc_s} = { | |
.bLength = sizeof(struct usb_desc_ep_t), | |
.bDescriptorType = USB_DESC_EP, | |
.ep_num = #@epnum, | |
.in = #{@direction.val}, | |
.type = #{@type.val}, | |
.wMaxPacketSize = #{@wMaxPacketSize.to_loc_s}, | |
.bInterval = #{@bInterval.to_loc_s} | |
}, | |
_end_ | |
end | |
end | |
class InterfaceDesc < DslItem | |
attr_accessor :ifacenum | |
field :bInterfaceClass | |
field :bInterfaceSubClass | |
field :bInterfaceProtocol | |
block :ep, EndpointDesc, :list => true, :optional => true | |
block :alternate, InterfaceDesc, :list => true, :optional => true | |
def initialize(name) | |
super() | |
@name = name | |
end | |
def renumber!(counts, alternatenum=0) | |
@ifacenum = counts[:iface] | |
@alternatenum = alternatenum | |
nc = counts.dup | |
@ep.each do |e| | |
nc = e.renumber!(nc) | |
end | |
@alternate.each_with_index do |a, i| | |
ac = a.renumber!(counts, i) | |
[:ep_in, :ep_out].each do |e| | |
if ac[e] > nc[e] | |
nc[e] = ac[e] | |
end | |
end | |
end | |
nc | |
end | |
def gen_defs | |
'' | |
end | |
def gen_desc_init | |
v = <<_end_ | |
.#{@name.to_loc_s} = { | |
.bLength = sizeof(struct usb_desc_iface_t), | |
.bDescriptorType = USB_DESC_IFACE, | |
.bInterfaceNumber = #@ifacenum, | |
.bAlternateSetting = #@alternatenum, | |
.bNumEndpoints = #{@ep.count}, | |
.bInterfaceClass = #{@bInterfaceClass.to_loc_s}, | |
.bInterfaceSubClass = #{@bInterfaceSubClass.to_loc_s}, | |
.bInterfaceProtocol = #{@bInterfaceProtocol.to_loc_s}, | |
.iInterface = 0 | |
}, | |
_end_ | |
v + @ep.map{|e| e.gen_desc_init}.join("\n") | |
end | |
end | |
class FunctionDesc < DslItem | |
block :interface, InterfaceDesc, :list => true | |
def renumber!(counts) | |
@var_name = "usb_function_#{counts[:iface]}" | |
@interface.each do |iface| | |
counts = iface.renumber!(counts) | |
counts[:iface] += 1 | |
end | |
counts | |
end | |
def get_desc_struct | |
"#{self.class::TypeName} #@var_name;" | |
end | |
def gen_defs | |
@interface.map{|i| i.gen_defs}.join("\n") | |
end | |
def gen_vars | |
'' | |
end | |
end | |
class ConfigDesc < DslItem | |
field :remote_wakeup, :default => 0 | |
field :self_powered, :default => 0 | |
field :bMaxPower, :default => 100 | |
field :initfun | |
block :function, FunctionDesc, :list => true | |
def renumber!(confignum) | |
@confignum = confignum | |
counts = {:iface => 0, :ep_in => 0, :ep_out => 0} | |
@function.each do |f| | |
counts = f.renumber!(counts) | |
end | |
@numinterfaces = counts[:iface] | |
@config_name = "usb_config_#@confignum" | |
@var_name = "usbd_config_#@confignum" | |
end | |
def gen_defs | |
@function.map{|f| f.gen_defs}.join + | |
<<_end_ | |
struct #@config_name { | |
struct usb_desc_config_t config; | |
#{@function.map{|f| f.get_desc_struct}.join("\n\t")} | |
}; | |
_end_ | |
end | |
def gen_vars | |
@function.map{|f| f.gen_vars}.join("\n") + | |
<<_end_ | |
static const struct #@config_name #@config_name = { | |
.config = { | |
.bLength = sizeof(struct usb_desc_config_t), | |
.bDescriptorType = USB_DESC_CONFIG, | |
.wTotalLength = sizeof(struct #@config_name), | |
.bNumInterfaces = #@numinterfaces, | |
.bConfigurationValue = #@confignum, | |
.iConfiguration = 0, | |
.one = 1, | |
.bMaxPower = #{@bMaxPower.to_loc_s} | |
}, | |
#{@function.map{|f| f.gen_desc_init}.join} | |
}; | |
static const struct usbd_config #@var_name = { | |
.init = #{@initfun.to_loc_s}, | |
.desc = &#@config_name.config | |
}; | |
_end_ | |
end | |
def get_var | |
@config_name | |
end | |
end | |
class DeviceDesc < DslItem | |
field :idVendor | |
field :idProduct | |
field :bcdDevice, :default => 0.0 | |
field :iManufacturer | |
field :iProduct | |
def initialize(name) | |
super() | |
@name = name | |
end | |
block :config, ConfigDesc, :list => true | |
def renumber! | |
@config.each_with_index do |c, i| | |
c.renumber!(i + 1) | |
end | |
end | |
def gen_defs | |
@config.map{|c| c.gen_defs}.join("\n") | |
end | |
def gen_vars | |
@config.map{|c| c.gen_vars}.join("\n") + | |
<<_end_ | |
static const struct usb_desc_dev_t #{@name.to_loc_s}_dev_desc = { | |
.bLength = sizeof(struct usb_desc_dev_t), | |
.bDescriptorType = USB_DESC_DEV, | |
.bcdUSB = { .maj = 2 }, | |
.bDeviceClass = USB_DEV_CLASS_SEE_IFACE, | |
.bDeviceSubClass = USB_DEV_SUBCLASS_SEE_IFACE, | |
.bDeviceProtocol = USB_DEV_PROTO_SEE_IFACE, | |
.bMaxPacketSize0 = EP0_BUFSIZE, | |
.idVendor = #{@idVendor.to_loc_s}, | |
.idProduct = #{@idProduct.to_loc_s}, | |
.bcdDevice = { .raw = 0 }, | |
.iManufacturer = 1, | |
.iProduct = 2, | |
.iSerialNumber = 3, | |
.bNumConfigurations = #{@config.length}, | |
}; | |
static const struct usb_desc_string_t * const #{@name.to_loc_s}_str_desc[] = { | |
USB_DESC_STRING_LANG_ENUS, | |
USB_DESC_STRING(#{@iManufacturer.to_loc_s{|s| "w#{s.inspect}"}}), | |
USB_DESC_STRING(#{@iProduct.to_loc_s{|s| "w#{s.inspect}"}}), | |
USB_DESC_STRING_SERIALNO, | |
NULL | |
}; | |
struct usbd #{@name.to_loc_s} = { | |
.dev_desc = &#{@name.to_loc_s}_dev_desc, | |
.string_descs = #{@name.to_loc_s}_str_desc, | |
.configs = { | |
#{@config.map{|c| "&#{c.get_var},"}.join("\n")} | |
NULL | |
} | |
}; | |
_end_ | |
end | |
end | |
class DescriptorRoot < DslItem | |
block :device, DeviceDesc, :list => true do |d| | |
@device ||= [] | |
@device << d | |
d.renumber! | |
end | |
def self.load(file) | |
src = File.read(file) | |
self.eval do | |
eval src | |
end | |
end | |
def gen | |
@device.map{|d| d.gen_defs}.join("\n") + | |
@device.map{|d| d.gen_vars}.join("\n") | |
end | |
end | |
require 'cdc' | |
require 'dfu' | |
if $0 == __FILE__ | |
require 'optparse' | |
outname = nil | |
OptionParser.new do |opts| | |
opts.on("-o", "--output FILE") do |fn| | |
outname = fn | |
end | |
end.parse! | |
r = DescriptorRoot.load(ARGV[0]) | |
if !(warnings = r.warnings).empty? | |
$stderr.puts warnings.join("\n") | |
exit 1 | |
end | |
if outname | |
File.open(outname, 'w') do |f| | |
f.puts r.gen | |
end | |
else | |
puts r.gen | |
end | |
end |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
class DFUDesc < FunctionDesc | |
TypeName = "struct dfu_function_desc" | |
child_block :dfu | |
def initialize | |
super | |
interface(:iface) { | |
bInterfaceClass :USB_DEV_CLASS_APP | |
bInterfaceSubClass :USB_DEV_SUBCLASS_APP_DFU | |
bInterfaceProtocol :USB_DEV_PROTO_DFU_DFU | |
} | |
end | |
def gen_desc_init | |
<<_end_ | |
.#@var_name = { | |
#{@interface.first.gen_desc_init} | |
.dfu = { | |
.bLength = sizeof(struct dfu_desc_functional), | |
.bDescriptorType = { | |
.id = 0x1, | |
.type_type = USB_DESC_TYPE_CLASS | |
}, | |
.will_detach = 1, | |
.manifestation_tolerant = 0, | |
.can_upload = 0, | |
.can_download = 1, | |
.wDetachTimeOut = 0, | |
.wTransferSize = USB_DFU_TRANSFER_SIZE, | |
.bcdDFUVersion = { .maj = 1, .min = 1 } | |
} | |
}, | |
_end_ | |
end | |
end |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
class LocationWrapper | |
attr_reader :location | |
def initialize(val, location) | |
@val = val | |
@location = location | |
end | |
def method_missing(method, *args, &block) | |
@val.send(method, *args, &block) | |
end | |
def to_s | |
@val.to_s | |
end | |
def inspect | |
@val.inspect | |
end | |
def to_loc_s | |
v = if location | |
"\n#line #@location\n" | |
else | |
"" | |
end | |
val = self | |
val = yield val if block_given? | |
v + val.to_s | |
end | |
end | |
class DslItem | |
class << self | |
def add_field(name, opts) | |
v = {} | |
begin | |
v = class_variable_get(:@@fields) | |
rescue NameError | |
class_variable_set(:@@fields, v) | |
end | |
v[name] = opts | |
end | |
def attach_lineno(val, lineno=caller[1]) | |
LocationWrapper.new(val, lineno) | |
end | |
def field(name, opts = {}, &action) | |
opts[:action] = action if action | |
add_field(name, opts) | |
define_method name do |val| | |
set_or_exec(name, val, opts) | |
end | |
end | |
def block(name, klass, opts = {}, &action) | |
klass.const_set(:Parent, self) | |
opts[:name] = name | |
opts[:klass] = klass | |
opts[:action] = action if action | |
add_field(name, opts) | |
field_alias(name, klass) | |
end | |
def field_alias(name_alias, klass) | |
opts = class_variable_get(:@@fields).values.find{|o| klass.ancestors.include? o[:klass]} | |
define_method name_alias do |*args, &block| | |
args = args.map do |a| | |
DslItem.attach_lineno(a) | |
end | |
val = klass.eval(*args, &block) | |
set_or_exec(opts[:name], val, opts) | |
end | |
end | |
def child_block(name) | |
superclass::Parent.field_alias(name, self) | |
end | |
def eval(*args, &block) | |
i = self.new(*args) | |
i.instance_exec(&block) | |
i.post_eval | |
i | |
end | |
end | |
def initialize | |
@warnings = [] | |
end | |
def post_eval | |
self.class.class_variable_get(:@@fields).each do |n, o| | |
if !instance_variable_defined?("@#{n}") | |
instance_variable_set("@#{n}", DslItem.attach_lineno(o[:default] || (o[:list] ? [] : nil), nil)) | |
end | |
end | |
rescue NameError | |
self.class.class_variable_set(:@@fields, {}) | |
end | |
def set_or_exec(name, val, opts) | |
location = caller[1] | |
wrapped_val = DslItem.attach_lineno(val, location) | |
if opts[:enum] | |
if !opts[:enum].include? val | |
@warnings << "#{location}: field `#{name}' value not one of #{opts[:enum].keys.map(&:inspect).join(", ")}" | |
else | |
wrapped_val.define_singleton_method :val do | |
opts[:enum][val] | |
end | |
end | |
end | |
if opts[:action] | |
instance_exec(val, &opts[:action]) | |
else | |
varname = "@#{name}".to_sym | |
if opts[:list] | |
if !instance_variable_get(varname) | |
instance_variable_set(varname, []) | |
end | |
instance_variable_get(varname) << wrapped_val | |
else | |
if instance_variable_defined?(varname) | |
@warnings << "#{location}: field `#{name}' redefined" | |
end | |
instance_variable_set(varname, wrapped_val) | |
end | |
end | |
wrapped_val | |
end | |
def warnings | |
r = @warnings.dup | |
self.class.class_variable_get(:@@fields).each do |name, opts| | |
val = instance_variable_get("@#{name}") | |
if !opts.has_key?(:default) && !opts[:optional] && !val | |
r << "#@location: required field `#{name}' undefined" | |
end | |
if val.respond_to? :warnings | |
r += val.warnings | |
elsif val.respond_to? :each | |
val.each do |v| | |
r += v.warnings if v.respond_to? :warnings | |
end | |
end | |
end | |
r | |
end | |
end |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
device(:foo) { | |
idVendor 0x2323 | |
idProduct 0x1 | |
iManufacturer "mchck.org" | |
iProduct "descriptor test" | |
config { | |
initfun :foo_init | |
cdc { | |
} | |
dfu { | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment